<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>17.1.3-收集动态节点</title>
</head>
<body>
    <!-- 虚拟节点参考例子 -->
    <div>
        <div id="foo">foo</div>
        <p class="bar">{{ text }}</p>
    </div>
</body>
    <script>
        /**
         * 在编译器生成的渲染函数代码中，并不会直接包含用来描述虚拟节点的数据结构，
         *  而是包含着用来创建虚拟 DOM 节点的辅助函数
        */

        /**
         * 为了让外层 Block 节点能够收集到内层动态节点，
         *  就需要一个栈结构的数据来临时存储内层的动态节点
        */
        // 动态节点栈
        const dynamicChildrenStack = []
        // 当前动态节点集合
        let currentDynamicChildren = null
        // openBlock 用来创建一个新的动态节点集合，并将该集合压入栈中
        function openBlock() {
            dynamicChildrenStack.push((currentDynamicChildren = []))
        }
        // closeBlock 用来将通过 openBlock 创建的动态节点集合从栈中弹出
        function closeBlock() {
            currentDynamicChildren = dynamicChildrenStack.pop()
        }

        /**
         * 把补丁标志理解为一系列数字标记
         */
       const PatchFlags = {
           TEXT: 1,  // 代表节点有动态的 textContent
           CLASS: 2, // 代表元素有动态的 class 绑定
           STYLE: 3  // 代表元素有动态的 style 绑定
           // 其他……
       }

       /**
        * 编译优化后，会生成带有补丁标志
        *  创建 p 标签的 createVNode 函数调用存在第四个参数，即 PatchFlags.TEXT
        *  这个参数就是所谓的补丁标志，它代表当前虚拟 DOM 节点是一个动态节点，
        *   并且动态因素是：具有动态的文本子节点
        * 
       */
        const obj = {
            // 1. 使用 createBlock 代替 createVNode 来创建 block
            // 2. 每当调用 createBlock 之前，先调用 openBlock
            render() {
                return (openBlock(), createBlock('div', null, [
                    createVNode('p', { class: 'foo' }, null, PatchFlags.TEXT /* patch flag */),
                    createVNode('p', { class: 'bar' }, null),
                ]))
            },

            createBlock(tag, props, children) {
                // block 本质上也是一个 vnode
                const block = createVNode(tag, props, children)
                // 将当前动态节点集合作为 block.dynamicChildren
                block.dynamicChildren = currentDynamicChildren

                // 关闭 block
                closeBlock()
                // 返回
                return block
            }
        }

        /**
         * createVNode 函数就是用来创建虚拟 DOM 节点的辅助函数
         *  - createVNode 函数内部，通常还会对 props 和 children 做一些额外的处理工作
         *  - createVNode 函数的调用是层层的嵌套结构，并且该函数的执行顺序是“内层先执行，外层后执行”
         * 
         * */ 
        function createVNode(tag, props, children, flags) {
            const key = props && props.key
            props && delete props.key

            const vnode = {
                tag,
                props,
                children,
                key,
                patchFlags: flags
            }

            if (typeof flags !== 'undefined' && currentDynamicChildren) {
                // 动态节点，将其添加到当前动态节点集合中
                currentDynamicChildren.push(vnode)
            }

            return vnode
        }


    </script>
    

</html>